package com.itx.gatewayx.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itx.gatewayx.common.constants.SystemConstants;
import com.itx.gatewayx.common.exception.ServiceException;
import com.itx.gatewayx.config.PasswordEncoderConfig.PasswordEncoder;
import com.itx.gatewayx.domain.SysRole;
import com.itx.gatewayx.domain.SysUser;
import com.itx.gatewayx.domain.dto.LoginDTO;
import com.itx.gatewayx.domain.vo.LoginVO;
import com.itx.gatewayx.mapper.SysMenuMapper;
import com.itx.gatewayx.mapper.SysRoleMapper;
import com.itx.gatewayx.mapper.SysUserMapper;
import com.itx.gatewayx.service.IAuthService;
import com.itx.gatewayx.utils.JwtUtils;
import com.itx.gatewayx.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 认证服务实现类
 *
 * @author itx
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements IAuthService {

    private final SysUserMapper userMapper;
    private final SysRoleMapper roleMapper;
    private final SysMenuMapper menuMapper;
    private final RedisUtils redisUtils;
    private final JwtUtils jwtUtils;
    private final PasswordEncoder passwordEncoder;

    @Override
    public Mono<LoginVO> login(LoginDTO loginDTO) {
        // 验证参数
        if (loginDTO == null || StrUtil.isBlank(loginDTO.getUsername()) || StrUtil.isBlank(loginDTO.getPassword())) {
            return Mono.error(new ServiceException("用户名和密码不能为空", HttpStatus.BAD_REQUEST.value()));
        }
        
        // 验证码校验
        if (StrUtil.isNotBlank(loginDTO.getCode()) && StrUtil.isNotBlank(loginDTO.getUuid())) {
            validateCaptcha(loginDTO.getUuid(), loginDTO.getCode());
        }
        
        // 根据用户名查询用户
        SysUser user = userMapper.selectOne(
                new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getUsername, loginDTO.getUsername())
                        .eq(SysUser::getDelFlag, 0));
        
        if (user == null) {
            return Mono.error(new ServiceException("用户不存在", HttpStatus.UNAUTHORIZED.value()));
        }
        
        // 校验用户状态
        if (user.getStatus() != SystemConstants.STATUS_NORMAL) {
            return Mono.error(new ServiceException("用户已被禁用", HttpStatus.UNAUTHORIZED.value()));
        }
        
        // 校验密码
        if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
            return Mono.error(new ServiceException("密码错误", HttpStatus.UNAUTHORIZED.value()));
        }
        
        // 查询用户角色
        List<SysRole> roles = roleMapper.selectRolesByUserId(user.getUserId());
        List<String> roleList = roles.stream().map(SysRole::getRoleCode).collect(Collectors.toList());
        
        // 查询用户权限
        List<String> permissions = menuMapper.selectMenuPermsByUserId(user.getUserId());
        
        // 构建JWT
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", user.getUsername());
        claims.put("roles", roleList);
        String userId = user.getUserId().toString();
        String token = jwtUtils.generateToken(userId, claims);
        String refreshToken = jwtUtils.generateRefreshToken(userId);
        
        // 保存用户信息到Redis
        String tokenKey = SystemConstants.LOGIN_TOKEN_KEY + userId;
        redisUtils.setCacheObject(tokenKey, token, jwtUtils.extractExpiration(token).getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        
        // 构建登录返回信息
        LoginVO loginVO = LoginVO.builder()
                .userId(user.getUserId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .token(token)
                .refreshToken(refreshToken)
                .roles(roleList)
                .permissions(permissions)
                .build();
        
        return Mono.just(loginVO);
    }

    @Override
    public Mono<Boolean> logout(String token) {
        if (StrUtil.isBlank(token)) {
            return Mono.just(false);
        }
        
        try {
            // 从token获取用户ID
            String userId = jwtUtils.extractSubject(token);
            if (StrUtil.isBlank(userId)) {
                return Mono.just(false);
            }
            
            // 从Redis删除token
            String tokenKey = SystemConstants.LOGIN_TOKEN_KEY + userId;
            redisUtils.deleteObject(tokenKey);
            
            // 将token加入黑名单
            String blacklistKey = SystemConstants.BLACK_TOKEN_KEY + token;
            redisUtils.setCacheObject(blacklistKey, "1", jwtUtils.extractExpiration(token).getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
            
            return Mono.just(true);
        } catch (Exception e) {
            log.error("注销登录失败: {}", e.getMessage(), e);
            return Mono.just(false);
        }
    }

    @Override
    public Mono<LoginVO> refreshToken(String refreshToken) {
        if (StrUtil.isBlank(refreshToken)) {
            return Mono.error(new ServiceException("刷新令牌不能为空", HttpStatus.BAD_REQUEST.value()));
        }
        
        if (!jwtUtils.validateToken(refreshToken)) {
            return Mono.error(new ServiceException("刷新令牌已过期", HttpStatus.UNAUTHORIZED.value()));
        }
        
        String userId = jwtUtils.extractSubject(refreshToken);
        if (StrUtil.isBlank(userId)) {
            return Mono.error(new ServiceException("无效的刷新令牌", HttpStatus.UNAUTHORIZED.value()));
        }
        
        // 查询用户
        SysUser user = userMapper.selectById(userId);
        if (user == null) {
            return Mono.error(new ServiceException("用户不存在", HttpStatus.UNAUTHORIZED.value()));
        }
        
        // 校验用户状态
        if (user.getStatus() != SystemConstants.STATUS_NORMAL) {
            return Mono.error(new ServiceException("用户已被禁用", HttpStatus.UNAUTHORIZED.value()));
        }
        
        // 查询用户角色
        List<SysRole> roles = roleMapper.selectRolesByUserId(user.getUserId());
        List<String> roleList = roles.stream().map(SysRole::getRoleCode).collect(Collectors.toList());
        
        // 查询用户权限
        List<String> permissions = menuMapper.selectMenuPermsByUserId(user.getUserId());
        
        // 生成新的token
        Map<String, Object> claims = new HashMap<>();
        claims.put("username", user.getUsername());
        claims.put("roles", roleList);
        String token = jwtUtils.generateToken(userId, claims);
        String newRefreshToken = jwtUtils.generateRefreshToken(userId);
        
        // 更新Redis中的token
        String tokenKey = SystemConstants.LOGIN_TOKEN_KEY + userId;
        redisUtils.setCacheObject(tokenKey, token, jwtUtils.extractExpiration(token).getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
        
        // 构建返回结果
        LoginVO loginVO = LoginVO.builder()
                .userId(user.getUserId())
                .username(user.getUsername())
                .nickname(user.getNickname())
                .avatar(user.getAvatar())
                .token(token)
                .refreshToken(newRefreshToken)
                .roles(roleList)
                .permissions(permissions)
                .build();
        
        return Mono.just(loginVO);
    }

    @Override
    public Mono<String> generateCaptcha() {
        // 生成验证码
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100, 4, 150);
        String code = captcha.getCode();
        String uuid = UUID.fastUUID().toString();
        
        // 将验证码存入Redis
        String verifyKey = SystemConstants.CAPTCHA_CODE_KEY + uuid;
        redisUtils.setCacheObject(verifyKey, code, SystemConstants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
        
        // 转换为base64编码的图片
        String base64Img = Base64.getEncoder().encodeToString(captcha.getImageBytes());
        
        // 构建返回信息
        Map<String, String> result = new HashMap<>();
        result.put("uuid", uuid);
        result.put("img", "data:image/png;base64," + base64Img);
        
        return Mono.just(JSON.toJSONString(result));
    }

    @Override
    public Mono<Boolean> verifyToken(String token) {
        if (StrUtil.isBlank(token)) {
            return Mono.just(false);
        }
        
        try {
            // 验证token有效性
            if (!jwtUtils.validateToken(token)) {
                return Mono.just(false);
            }
            
            // 从token获取用户ID
            String userId = jwtUtils.extractSubject(token);
            if (StrUtil.isBlank(userId)) {
                return Mono.just(false);
            }
            
            // 检查token是否在Redis中存在
            String tokenKey = SystemConstants.LOGIN_TOKEN_KEY + userId;
            if (!redisUtils.hasKey(tokenKey)) {
                return Mono.just(false);
            }
            
            // 检查token是否在黑名单中
            String blacklistKey = SystemConstants.BLACK_TOKEN_KEY + token;
            if (Boolean.TRUE.equals(redisUtils.hasKey(blacklistKey))) {
                return Mono.just(false);
            }
            
            return Mono.just(true);
        } catch (Exception e) {
            log.error("验证token失败: {}", e.getMessage(), e);
            return Mono.just(false);
        }
    }

    /**
     * 验证验证码
     */
    private void validateCaptcha(String uuid, String code) {
        String verifyKey = SystemConstants.CAPTCHA_CODE_KEY + uuid;
        String captcha = redisUtils.getCacheObject(verifyKey);
        redisUtils.deleteObject(verifyKey);
        
        if (captcha == null) {
            throw new ServiceException("验证码已过期", HttpStatus.BAD_REQUEST.value());
        }
        
        if (!code.equalsIgnoreCase(captcha)) {
            throw new ServiceException("验证码错误", HttpStatus.BAD_REQUEST.value());
        }
    }
} 